package git.soulbgm.service.impl;

import cn.hutool.extra.ssh.JschUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import git.soulbgm.common.enums.Status;
import git.soulbgm.common.okhttp.OkHttpUtil;
import git.soulbgm.pojo.NodeInfo;
import git.soulbgm.pojo.ServiceInfo;
import git.soulbgm.pojo.SoftwareInfo;
import git.soulbgm.service.NodeInfoService;
import git.soulbgm.service.ServiceInfoService;
import git.soulbgm.service.SoftwareInfoService;
import git.soulbgm.service.StartupService;
import git.soulbgm.utils.LogHelper;
import git.soulbgm.utils.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.Map;

import static git.soulbgm.common.MonitorConstants.*;

/**
 * 启动服务
 *
 * @author SoulBGM
 * @date 2021/02/02
 */
@Service
public class StartupServiceImpl implements StartupService {

    /**
     * utf-8编码
     */
    private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;

    private final LogHelper log = new LogHelper(getClass());

    @Autowired
    private NodeInfoService nodeInfoService;

    @Autowired
    private ServiceInfoService serviceInfoService;

    @Autowired
    private SoftwareInfoService softwareInfoService;

    /**
     * 异步启动监控代理程序
     *
     * @param nodeCode 节点/主机编号
     */
    @Override
    @Async
    public void asyncStartupNode(Integer nodeCode) {
        try {
            startupAndShutdownNode(nodeCode, ONLINE, false);
        } catch (Exception e) {
            log.error("启动主机失败", e);
        }
    }

    /**
     * 启动和关闭节点监控代理程序
     *
     * @param nodeCode 节点/主机编号
     * @param flag     上下线标识
     * @param isManual 是否手动
     */
    @Override
    public String startupAndShutdownNode(Integer nodeCode, int flag, boolean isManual) throws JSchException {
        NodeInfo nodeInfo = new NodeInfo();
        nodeInfo.setNodeCode(nodeCode);
        NodeInfo info = nodeInfoService.getOne(new QueryWrapper<>(nodeInfo));
        if (SYSTEM_LINUX != info.getOperatingSystem()) {
            return NOT_SUPPORT_OPERATION;
        }
        if (!isManual) {
            if (Status.NO.code.equals(info.getSelfStart())) {
                return NOT_SELF_START;
            }
        }
        Session session = JschUtil.getSession(info.getIp(), info.getSshPort(), info.getUsername(), info.getPassword());
        String command = StringUtil.format("bash {}", flag == ONLINE ? info.getStartupScriptPath() : info.getStopScriptPath());
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        String result = JschUtil.exec(session, command, CHARSET_UTF8, output);
        String errStr = getString(output);
        if (StringUtil.isNotEmpty(errStr)) {
            return errStr;
        }
        return result;
    }

    /**
     * 将输出流转换为字符串
     *
     * @param output  输出流
     * @return {@link String}
     */
    private static String getString(ByteArrayOutputStream output) {
        if (output != null) {
            byte[] bytes = output.toByteArray();
            return new String(bytes, CHARSET_UTF8);
        }
        return null;
    }

    /**
     * 启动服务
     *
     * @param nodeCode    节点/设备编号
     * @param serviceCode 服务编号
     */
    @Override
    @Async
    public void startupService(Integer nodeCode, Integer serviceCode) {
        startupAndShutdownService(nodeCode, serviceCode, ONLINE, false);
    }

    /**
     * 启动和关闭服务
     *
     * @param nodeCode    节点/主机编号
     * @param serviceCode 服务编号
     * @param flag        上下线标识
     * @param isManual    是否手动
     * @return {@link String}
     */
    @Override
    public String startupAndShutdownService(Integer nodeCode, Integer serviceCode, int flag, boolean isManual) {
        NodeInfo nodeInfo = new NodeInfo();
        nodeInfo.setNodeCode(nodeCode);
        NodeInfo info = nodeInfoService.getOne(new QueryWrapper<>(nodeInfo));
        if (SYSTEM_LINUX != info.getOperatingSystem()) {
            return NOT_SUPPORT_OPERATION;
        }
        ServiceInfo serviceInfo = new ServiceInfo();
        serviceInfo.setNodeCode(nodeCode);
        serviceInfo.setServiceCode(serviceCode);
        ServiceInfo dbServiceInfo = serviceInfoService.getOne(new QueryWrapper<>(serviceInfo));
        if (!isManual) {
            if (Status.NO.code.equals(dbServiceInfo.getSelfStart())) {
                return NOT_SELF_START;
            }
        }
        String url = StringUtil.format("http://{}:{}{}", info.getIp(), info.getPort(), EXEC_URL);
        return OkHttpUtil.post(url, setParams(flag == ONLINE ? dbServiceInfo.getStartupScriptPath() : dbServiceInfo.getStopScriptPath()), dbServiceInfo.getEstimatedStartupTime());
    }

    /**
     * 启动第三方软件
     *
     * @param nodeCode     节点/设备编号
     * @param softwareName 软件的名字
     * @param processName  进程名称
     */
    @Override
    @Async
    public void startupSoftware(Integer nodeCode, String softwareName, String processName) {
        startupAndShutdownSoftware(nodeCode, softwareName, processName, ONLINE, false);
    }

    /**
     * 启动和关闭软件
     *
     * @param nodeCode     节点/主机编号
     * @param softwareName 软件名字
     * @param processName  进程名称
     * @param flag         上下线标识
     * @param isManual     是否手动
     * @return {@link String}
     */
    @Override
    public String startupAndShutdownSoftware(Integer nodeCode, String softwareName, String processName, int flag, boolean isManual) {
        NodeInfo nodeInfo = new NodeInfo();
        nodeInfo.setNodeCode(nodeCode);
        NodeInfo info = nodeInfoService.getOne(new QueryWrapper<>(nodeInfo));
        if (SYSTEM_LINUX != info.getOperatingSystem()) {
            return NOT_SUPPORT_OPERATION;
        }
        SoftwareInfo softwareInfo = new SoftwareInfo();
        softwareInfo.setNodeCode(nodeCode);
        softwareInfo.setSoftwareName(softwareName);
        softwareInfo.setProcessName(processName);
        SoftwareInfo dbSoftwareInfo = softwareInfoService.getOne(new QueryWrapper<>(softwareInfo));
        if (!isManual) {
            if (Status.NO.code.equals(dbSoftwareInfo.getSelfStart())) {
                return NOT_SELF_START;
            }
        }
        String url = StringUtil.format("http://{}:{}{}", info.getIp(), info.getPort(), EXEC_URL);
        return OkHttpUtil.post(url, setParams(flag == ONLINE ? dbSoftwareInfo.getStartupScriptPath() : dbSoftwareInfo.getStopScriptPath()), dbSoftwareInfo.getEstimatedStartupTime());
    }

    /**
     * 设置参数
     *
     * @param scriptPath 脚本路径
     * @return 参数
     */
    private Map<String, Object> setParams(String scriptPath) {
        Map<String, Object> params = new LinkedHashMap<>(1);
        params.put("command", scriptPath);
        return params;
    }

}
